package com.ruoyi.ai.service;

import com.alibaba.fastjson.JSONObject;
import com.ruoyi.ai.dto.ChatRequestDTO;
import com.ruoyi.ai.pojo.Operation;
import com.ruoyi.ai.tools.ChatTools;
import com.ruoyi.ai.ws.handler.ChatWebSocketHandler;
import jakarta.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemory;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.model.Media;
import org.springframework.ai.model.tool.ToolCallingChatOptions;
import org.springframework.ai.model.tool.ToolCallingManager;
import org.springframework.ai.model.tool.ToolExecutionResult;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.definition.ToolDefinition;
import org.springframework.ai.tool.method.MethodToolCallback;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
import org.springframework.util.MimeType;
import org.springframework.util.ReflectionUtils;
import reactor.core.publisher.Flux;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @author coach tam
 * @email 327395128@qq.com
 * @values 坚持灵活 灵活坚持
 * @since 2025/3/22
 */
@Slf4j
@Service
public class ChatServiceImpl implements ChatService {

    private static String PROMPT_TEMPLATE = """
            你是雅橙科技教学助手，由龙哥研制，正在回答用户的提问。
            所有的回答都只能通过中文回答。
            所有回答的格式都是md格式。
            我们公司的口号是"用心做教育"，你只能回答编程问题以及教学相关的业务，。
            使用来自"文档"部分的信息来提供准确的答案,尽可能保留原"文档"的内容。如果不确定或在"文档"部分找不到答案，只需表明你不知道答案即可。
            
            问题:
            {input}
            """;
    public static final String FORWARD = "跳转";


    @Autowired
    @Qualifier("chatModel")
    private OpenAiChatModel chatModel;
    @Autowired
    @Qualifier("visionModel")
    private OpenAiChatModel visionModel;

    @Autowired
    private ChatTools chatTools;

    @Autowired
    private VectorService vectorService;

//    // 配置 ChatMemory
//    private ChatMemory chatMemory = new InMemoryChatMemory();

    @Autowired
    private ChatMemory chatMemory;
    // 支持的操作类型
    private final static List<String> supportOperationList = Arrays.asList("启用", "禁用", "查询", "删除", "新增", "跳转");
    public static final String UNKNOW_OPER = "未知unknown";
    public static final String NOT_SUPPORT_OPER_TIPS = String.format("无法解析指令，目前支持指令有:%s,如要操作数据请使用格式如下：禁用id为1的用户", supportOperationList);
    private ToolCallback[] toolCallbacksSet;

    @PostConstruct
    public void init() {
        // 创建工具回调集合
        toolCallbacksSet = new ToolCallback[]{
                createToolCallback(chatTools, "getCurrentDateTime", "获取用户当前的日期信息"),
                createToolCallback(chatTools, "queryTeachingPlan", "查询课表"),
                createToolCallback(chatTools, "route", "跳转页面"),
                createToolCallback(chatTools, "queryAssistantName", "查询助手信息"),
                createToolCallback(chatTools, "queryUserName", "查询用户信息和学习情况"),
                createToolCallback(chatTools, "queryCourse", "查询课程信息")
        };
        log.info("工具回调集合创建完成");
    }

    @Override
    public Flux<ChatResponse> chatWithTools(ChatRequestDTO chatRequestDTO) {
        String message = chatRequestDTO.message();
        boolean useHistoryChat = chatRequestDTO.useHistoryChat();
        boolean useTools = chatRequestDTO.useTools();
        boolean useLocalKnowledge = chatRequestDTO.useLocalKnowledge();

        log.info("开始处理用户消息: {}", message);

        // 解析消息中的图片URL
        List<String> imageUrls = extractImageUrls(message);
        // 清理消息文本，去除图片标记
        String cleanMessage = cleanMessageText(message);

        log.info("提取到图片数量: {}", imageUrls.size());
        log.info("清理后的消息文本: {}", cleanMessage);

        return Flux.deferContextual(ctx -> {
            log.info("创建ToolCallingManager");
            ToolCallingManager toolCallingManager = ToolCallingManager.builder().build();

            String userId = ctx.get("userId");
            String username = ctx.get("userName");
            log.info("获取用户ID: {}", userId);

            log.info("构建提示参数");
            // 构建提示并发送请求
            Map<String, Object> promptParams = new java.util.HashMap<>(Map.of(
                    "input", cleanMessage
            ));

            StringBuilder promptStr = new StringBuilder(PROMPT_TEMPLATE);

            //获取历史对话
            if (useHistoryChat) {
                List<Message> historyMessages = chatMemory.get(userId, 10);
                log.info("获取历史对话记录, 数量: {}", historyMessages.size());
                if (CollectionUtils.isNotEmpty(historyMessages)) {
                    promptParams.put("history", historyMessages);
                    promptStr.append("""
                                历史对话：
                                {history}
                            """);
                }
            }


            //获取当本地知识库文档
            if (useLocalKnowledge) {
                log.info("开始从向量数据库搜索相关文档");
                List<String> contentList = vectorService.searchQuestion(message);
                if (CollectionUtils.isNotEmpty(contentList)) {
                    log.info("向量搜索完成, 获取到文档数量: {}", contentList.size());
                    promptParams.put("documents", contentList);
                    promptStr.append("""
                            文档:
                            {documents}
                            """);
                }
            }
            ToolCallingChatOptions.Builder chatOptionBuilder = ToolCallingChatOptions.builder()
                    .internalToolExecutionEnabled(false)
                    .maxTokens(1000)
                    .frequencyPenalty(1.5)
                    .temperature(0.75);
            // 构建工具回调集合
            if (useTools) {
                chatOptionBuilder.toolCallbacks(toolCallbacksSet);
            }
            log.info("创建提示模板");
            // 创建提示模板
            PromptTemplate promptTemplate = new PromptTemplate(promptStr.toString());

            log.info("创建Prompt对象");
            ChatOptions chatOptions = chatOptionBuilder.build();

            Prompt prompt;
            // 选择合适的模型
            OpenAiChatModel selectedModel;

            if (!imageUrls.isEmpty()) {
                log.info("检测到图片，准备创建多模态消息");
                selectedModel = visionModel;
                // 创建包含图片的多模态消息
                List<Media> mediaList = new ArrayList<>();
                for (String imageUrl : imageUrls) {
                    try {
                        // 创建媒体对象
                        Media media = Media.builder().mimeType(MimeType.valueOf("image/png")).data(imageUrl).build();
                        mediaList.add(media);
                    } catch (Exception e) {
                        log.error("加载图片失败: " + imageUrl, e);
                    }
                }

                // 创建多模态用户消息
                UserMessage userMessage = new UserMessage(cleanMessage, mediaList);
                // 创建包含多模态消息的Prompt
                prompt = new Prompt(userMessage, chatOptions);
            } else {
                selectedModel = chatModel;
                // 创建普通文本Prompt
                prompt = promptTemplate.create(promptParams, chatOptions);
            }

            log.info("发送初始请求到AI模型");
            ChatResponse chatResponse = selectedModel.call(prompt);
            log.info("收到AI模型初始响应");

            // 处理工具调用
            int toolCallCount = 0;
            while (chatResponse.hasToolCalls()) {
                toolCallCount++;
                log.info("检测到工具调用, 第{}次工具调用", toolCallCount);
                log.info("执行工具调用");
                ToolExecutionResult toolExecutionResult = toolCallingManager.executeToolCalls(prompt, chatResponse);
                log.info("工具调用执行完成, 更新对话历史");
                prompt = new Prompt(toolExecutionResult.conversationHistory(), chatOptions);
                log.info("发送更新后的请求到AI模型");
                chatResponse = selectedModel.call(prompt);
                log.info("收到AI模型更新响应");
            }

            if (toolCallCount > 0) {
                log.info("工具调用处理完成, 共执行了{}次工具调用", toolCallCount);
            } else {
                log.info("无工具调用需要处理");
            }

            log.info("开始流式处理AI响应");
            StringBuilder sb = new StringBuilder();
            return selectedModel.stream(prompt)
                    .doOnNext(cr -> {
                        //追加响应
                        String text = cr.getResults().get(0).getOutput().getText();
                        sb.append(text);
                        log.debug("接收到流式响应片段: {}", text);
                    }).doOnComplete(() -> {
                        //如何判断还需要额外操作
                        handleAIResponse(message, sb, username, userId);
                    });
        });
    }

    private void handleAIResponse(String message, StringBuilder sb, String username, String userId) {
        Operation entity = null;
        try {
            entity = ChatClient.create(chatModel).prompt()
                    .user(u -> u.text("""
                                    根据用户的输入:{message},判断用户的操作,目前只支持的操作如下:{support},自动识别以支持的操作类型和参数。
                                    如没有匹配到支持的操作，将"未知" 封装到 operation字段中
                                    """)
                            .param("message", message)
                            .param("support", supportOperationList)
                    )
                    .call()
                    .entity(Operation.class);
        } catch (Exception e) {
            sendChatMessage(NOT_SUPPORT_OPER_TIPS, username);
            return;
        }

        String operation = entity.operation();
        if (!UNKNOW_OPER.contains(operation)) {
            // 构建操作消息
            JSONObject operationMessage = new JSONObject();
            operationMessage.put("timestamp", System.currentTimeMillis());
            if (FORWARD.contains(operation)) {
                // 检测页面跳转指令
                String regex = "\\([^)]+\\)"; // 匹配路径部分
                Pattern pattern = Pattern.compile(regex);
                Matcher matcher = pattern.matcher(sb.toString());

                log.info("需要跳转页面, 跳转页面");
                //通知浏览器跳转页面
                matcher.find();
                String vue = matcher.group().replace("(", "").replace(")", "");
                operationMessage.put("type", "navigation");
                operationMessage.put("path", vue);
                // 发送操作消息
                sendChatMessage("我正在为你跳转到[" + vue + "]，请稍候...", username);
            } else if(supportOperationList.contains(operation)){
                Map<String, Object> params = entity.params();
                operationMessage.put("type", "operation");
                operationMessage.put("operation", operation);
                operationMessage.put("params", params);
                sendChatMessage("我正在为你执行[" + message + "]操作，请稍候...", username);
            }
            ChatWebSocketHandler.sendMessage(username, operationMessage.toJSONString());
        }

        log.info("流式响应接收完成, 最终响应长度: {}", sb.length());
        //更新历史记录
        log.info("更新对话历史记录 - 添加用户消息");
        chatMemory.add(userId, new UserMessage(message));
        log.info("更新对话历史记录 - 添加助手响应");
        chatMemory.add(userId, new AssistantMessage(sb.toString()));
        log.info("对话历史记录更新完成");
    }

    private static void sendChatMessage(String message, String username) {
        // 同时发送文本消息告知用户
        JSONObject chatMessage = new JSONObject();
        chatMessage.put("type", "chat");
        chatMessage.put("content", message);
        ChatWebSocketHandler.sendMessage(username, chatMessage.toJSONString());
    }

    /**
     * 创建工具回调
     *
     * @param toolObject  工具对象
     * @param methodName  方法名
     * @param description 描述
     * @return 工具回调
     */
    private ToolCallback createToolCallback(Object toolObject, String methodName, String description) {
        log.info("创建工具回调: 方法={}, 描述={}", methodName, description);
        Method method = ReflectionUtils.findMethod(toolObject.getClass(), methodName);
        if (method == null) {
            log.error("未找到方法: {}.{}", toolObject.getClass().getSimpleName(), methodName);
            throw new IllegalArgumentException("Method not found: " + methodName);
        }
        log.info("找到方法: {}.{}", toolObject.getClass().getSimpleName(), methodName);
        ToolCallback callback = MethodToolCallback.builder()
                .toolDefinition(ToolDefinition.builder(method).description(description).build())
                .toolMethod(method)
                .toolObject(toolObject)
                .build();
        log.info("工具回调创建完成: {}", callback);
        return callback;
    }

    /**
     * 从消息文本中提取图片URL
     *
     * @param message 消息文本
     * @return 图片URL列表
     */
    private List<String> extractImageUrls(String message) {
        List<String> imageUrls = new ArrayList<>();
        // 匹配Markdown格式的图片链接 ![图片](http://example.com/image.jpg)
        Pattern pattern = Pattern.compile("!\\[.*?\\]\\((.*?)\\)");
        Matcher matcher = pattern.matcher(message);

        while (matcher.find()) {
            String imageUrl = matcher.group(1);
            imageUrls.add(imageUrl);
            log.info("提取到图片URL: {}", imageUrl);
        }

        return imageUrls;
    }

    /**
     * 清理消息文本，去除图片标记
     *
     * @param message 原始消息文本
     * @return 清理后的消息文本
     */
    private String cleanMessageText(String message) {
        // 替换Markdown格式的图片链接为空字符串
        return message.replaceAll("!\\[.*?\\]\\(.*?\\)", "").trim();
    }
}
